<html>
<head>
<meta charset="utf8">
<title>3D in canvas</title>
<link type="text/css" href="../../resources/webgl-tutorials.css" rel="stylesheet" />
<style>
body {
    margin: 0;
}
#movie {
    width: 100vw;
    height: 100vh;
}
@media (prefers-color-scheme: dark) {
  canvas { 
    background: #444;
  }
}
</style>
</head>
<body>
<h1 class="description">Moving the Camera</h1>
<div id="movie"></div>
</body>
<!-- this is included only for iframe and requestAnimationFrame support -->
<!--
for most samples webgl-utils only provides shader compiling/linking and
canvas resizing because why clutter the examples with code that's the same in every sample.
See https://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html
and https://webglfundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
for webgl-utils, m3, m4, and webgl-lessons-ui.
-->
<script src="../../resources/webgl-utils.js"></script>
<script src="../../resources/lessons-helper.js"></script> <!-- you can and should delete this script. it is only used on the site to help with errors -->
<script src="bonsai-0.4.1.min.js"></script>
<script>
"use strict";

function main() {
  var movieElem = document.querySelector('#movie');
  var areaWidth = movieElem.clientWidth;
  var areaHeight = movieElem.clientHeight;

  var code = function() {

    var getURLOptions = function(s, obj) {
      var q = s.indexOf("?");
      var e = s.indexOf("#");
      if (e < 0) {
        e = s.length;
      }
      var query = s.substring(q + 1, e);
      var pairs = query.split("&");
      for (var ii = 0; ii < pairs.length; ++ii) {
        var keyValue = pairs[ii].split("=");
        var key = keyValue[0];
        var value = decodeURIComponent(keyValue[1]);
        obj[key] = value;
      }
    };

    var options = {
      mode: "0",
    };
    getURLOptions(stage.options.initialData.url, options);

    var mode = parseInt(options.mode);

    var fRadius = 60;
    var camRadius = (mode === 3) ? fRadius * 1.5 : 120;
    var areaWidth = 400;
    var areaHeight = 300;
    var gridSpacing = 20;
    var gridsAcross = Math.ceil(areaWidth / gridSpacing);
    var gridsDown = Math.ceil(areaHeight / gridSpacing);
    var gridLines = Math.max(gridsAcross, gridsDown);
    var colors = stage.options.initialData.colors;

    stage.setBackgroundColor(color(colors.background));
    stage.setFramerate(60);

    var top = new Group()
        .addTo(stage)
        .attr({
           scale: stage.options.initialData.areaWidth / 400,
        });

    // console.log(areaWidth / 400);

    var yoff = (mode === 2) ? 90 : 0;

    var grid = new Group()
        .addTo(top)
        .attr({
          x: areaWidth / 2,
          y: areaHeight / 2 + yoff,
        });

    var world = new Group()
        .addTo(top)
        .attr({
          x: areaWidth / 2,
          y: areaHeight / 2 - (mode === 2 ? camRadius : 0) + yoff,
        });

    if (mode === 3) {
      new Circle(0, 0, fRadius)
         .addTo(world)
         .attr({
           fillColor: "transparent",
           strokeColor: colors.innerRadius,
           strokeWidth: 1,
         });

      new Circle(0, 0, camRadius)
         .addTo(world)
         .attr({
           fillColor: "transparent",
           strokeColor: colors.outerRadius,
           strokeWidth: 1,
         });
    }
    var base = new Group()
        .addTo(top)
        .attr({
          x: areaWidth / 2,
          y: areaHeight / 2 + yoff,
        });

    var axis = new Group()
        .addTo(top)
        .attr({
          x: areaWidth / 2,
          y: areaHeight / 2,
        });

    for (var ii = -gridLines; ii < gridLines; ++ii) {
      var c = ii ? colors.grid : colors.gridHighlight;
      new Rect(-areaWidth, ii * gridSpacing, areaWidth * 2, 0.5)
          .addTo(grid)
          .attr('fillColor', c);
      new Rect(ii * gridSpacing, -areaHeight, 0.5, areaHeight * 2)
          .addTo(grid)
          .attr('fillColor', c);
    }
    var text = [
      { text: "-x", x: -areaWidth  / 2 + 2,           y: 2 + yoff },
      { text: "+x", x:  areaWidth  / 2 - gridSpacing, y: 2 + yoff },
      { text: "-z", y: -areaHeight / 2 + 2,           x: 2 },
      { text: "+z", y:  areaHeight / 2 - gridSpacing, x: 2 },
    ];
    text.forEach(function(t) {
      new Text(t.text)
          .addTo(axis)
          .attr({
            x: t.x,
            y: t.y,
            textFillColor: colors.axisLabel,
          });
    });


    function degToRad(d) {
      return d * Math.PI / 180;
    }

    if (mode === 3) {
      const ip = new Group()
         .addTo(world)
         .attr({
           rotation: degToRad(-35),
         });
      new Path()
        .addTo(ip)
        .moveTo(0, 0)
        .lineTo(fRadius, 0)
        .attr({
          fillColor: "transparent",
          strokeColor: colors.innerRadius,
          strokeWidth: 1,
        });
      new Text("radius")
        .addTo(ip)
        .attr({
          textFillColor: colors.innerRadius,
          x: fRadius * .1,
          y: -10,
          fontSize: 12,
        });
      const cp = new Group()
         .addTo(base)
         .attr({
           rotation: degToRad(90),
         });
      new Path()
        .addTo(cp)
        .moveTo(0, 0)
        .lineTo(camRadius, 0)
        .attr({
          fillColor: "transparent",
          strokeColor: colors.outerRadius,
          strokeWidth: 1,
        });
      new Text("radius*1.5")
        .addTo(cp)
        .attr({
          textFillColor: colors.outerRadius,
          x: camRadius * 0.05,
          y: -12,
          fontSize: 12,
        });

    }

    var createCamera = function() {
      var c = new Path()
          .moveTo(0, 0)
          .lineTo(10, 0)
          .lineTo(10, 20)
          .lineTo(-10, 20)
          .lineTo(-10, 0)
          .lineTo(0, 0)
          .lineTo(-10, -10)
          .lineTo(10, -10)
          .lineTo(0, 0)
          .closePath()
          .fill(colors.camera);
      return c;
    };

    var createF = function() {
      var f = new Group();
      new Rect(-15,  0 - 25, 30, 10).addTo(f).attr('fillColor', colors.f);
      new Rect(-15, 10 - 25, 10, 10).addTo(f).attr('fillColor', colors.f);
      new Rect(-15, 20 - 25, 20, 10).addTo(f).attr('fillColor', colors.f);
      new Rect(-15, 30 - 25, 10, 20).addTo(f).attr('fillColor', colors.f);
      f.attr({scale: mode === 3 ? .5 : 1});
      return f;
    };

    var numF = 5;
    for (var ii = 0; ii < numF; ++ii) {
      var f = createF();
      var a = ii * Math.PI * 2 / numF;
      f.addTo(world)
       .attr('x', Math.cos(a) * fRadius)
       .attr('y', Math.sin(a) * fRadius);
    }

    var cam = createCamera();
    cam.addTo(base).attr({
      y: mode === 2 ? 0 : camRadius,
    });

    if (mode === 2) {
      var frustum = new Path()
          .moveTo( -20,  -20)
          .lineTo(-170, -210)
          .lineTo( 170, -210)
          .lineTo(  20,  -20)
          .closePath()
          .fill("#80FF4080")
          .addTo(base);
    }

    switch (mode) {
      case 1:
      case 2:
        world.animate(new KeyframeAnimation('8s', {
          '0%':   { rotation: 0},
          '100%': { rotation: Math.PI * 2 * (mode === 1 || mode === 2 ? -1 : 1) }
        }, {repeat: 100000000}));
        break;
      case 3:
      default:
        base.animate(new KeyframeAnimation('8s', {
          '0%':   { rotation: 0},
          '100%': { rotation: Math.PI * 2 }
        }, {repeat: 100000000}));
        break;
    }
  };

  const darkColors = {
    grid: '#666',
    gridHighlight: '#FFF',
    axisLabel: '#FFF',
    background: '#444',
    f: '#F4F',
    camera: '#48F',
    innerRadius: '#AAA',
    outerRadius: '#F44',
  };
  const lightColors = {
    grid: '#CCC',
    gridHighlight: '#000',
    axisLabel: '#000',
    background: '#FFF',
    f: 'purple',
    camera: 'blue',
    innerRadius: '#888',
    outerRadius: '#F00',
  };
  const darkMatcher = window.matchMedia("(prefers-color-scheme: dark)");
  const isDarkMode = darkMatcher.matches;
  const colors = isDarkMode ? darkColors : lightColors;

  bonsai.run(movieElem, {
    code: code,
    width: areaWidth,
    height: areaHeight,
    initialData: {
      url: window.location.href.toString(),
      areaWidth: areaWidth,
      areaHeight: areaHeight,
      colors: isDarkMode ? darkColors : lightColors,
    },
  });
}

main();
</script>
</html>
